/*******************************************************************************
*
*   FILE NAME:
*       GraphicsDevice.cpp - GraphicsDevice class implementation
*
*   DESCRIPTION:
*       Performs primitive-based rendering, creates resources, handles
*       system-level variables, adjusts gamma ramp levels, and creates shaders.
*
*******************************************************************************/
#include "GraphicsDevice.h"

GraphicsDevice::GraphicsDevice( GraphicsAdapter& graphics_adapter, const PresentationParameters& presentation_parameters )
    : mBackBuffer( NULL ),
      mDevice( NULL ),
      mDeviceContext( NULL ),
      mGraphicsAdapter( graphics_adapter ),
      mPresentationParameters( presentation_parameters ),
      mRenderTarget( NULL ),
      mSwapChain( NULL )
{
    Reset( mPresentationParameters, mGraphicsAdapter );
}

GraphicsDevice::~GraphicsDevice( void )
{
    if( mBackBuffer != NULL )
    {
        delete mBackBuffer;
    }
    if( mSwapChain != NULL )
    {
        mSwapChain->Release();
    }
    if( mDevice != NULL )
    {
        mDevice->Release();
    }
    if( mDeviceContext != NULL )
    {
        mDeviceContext->Release();
    }
}

void GraphicsDevice::Clear( const Color& color )
{
    mDeviceContext->ClearRenderTargetView( mRenderTarget->mRenderTargetView, ( float32* ) &color );
}

void GraphicsDevice::Present( void )
{
    mSwapChain->Present( 0, 0 );
}

void GraphicsDevice::Reset( void )
{
    Reset( mPresentationParameters, mGraphicsAdapter );
}

void GraphicsDevice::Reset( const PresentationParameters& presentation_parameters )
{
    Reset( presentation_parameters, mGraphicsAdapter );
}

void GraphicsDevice::Reset( const PresentationParameters& presentation_parameters, GraphicsAdapter& graphics_adapter )
{
    if( mBackBuffer != NULL )
    {
        delete mBackBuffer;
    }
    if( mSwapChain != NULL )
    {
        mSwapChain->Release();
    }
    if( mDevice != NULL )
    {
        mDevice->Release();
    }
    if( mDeviceContext != NULL )
    {
        mDeviceContext->Release();
    }

    DXGI_SWAP_CHAIN_DESC scd;
    ZeroMemory( &scd, sizeof( DXGI_SWAP_CHAIN_DESC ) );
    scd.BufferCount = 1;
    scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scd.BufferDesc.Height = mPresentationParameters.BackBufferHeight;
    scd.BufferDesc.Width = mPresentationParameters.BackBufferWidth;
    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    scd.OutputWindow = ( HWND ) mPresentationParameters.DeviceWindowHandle;
    scd.SampleDesc.Count = mPresentationParameters.MultiSampleCount;
    scd.Windowed = !mPresentationParameters.IsFullScreen;

    D3D11CreateDeviceAndSwapChain( NULL,
                                   D3D_DRIVER_TYPE_HARDWARE,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   D3D11_SDK_VERSION,
                                   &scd,
                                   &mSwapChain,
                                   &mDevice,
                                   NULL,
                                   &mDeviceContext );

    // set the back buffer as the default render target
    mBackBuffer = new RenderTarget2D( *this, mPresentationParameters.BackBufferWidth, mPresentationParameters.BackBufferHeight );
    mBackBuffer->mRenderTargetView->Release();
    mBackBuffer->mTexture->Release();
    mSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* ) &( mBackBuffer->mTexture ) );
    mDevice->CreateRenderTargetView( ( mBackBuffer->mTexture ), NULL, &( mBackBuffer->mRenderTargetView ) );
    SetRenderTarget( NULL );
    
    // create viewport and set it
    mViewport.Bounds = mPresentationParameters.Bounds;
    mViewport.Height = mPresentationParameters.BackBufferHeight;
    mViewport.MaxDepth = 1.0f;
    mViewport.MinDepth = 0.0f;
    mViewport.TitleSafeArea = mPresentationParameters.Bounds;
    mViewport.Width = mPresentationParameters.BackBufferWidth;
    mViewport.X = 0;
    mViewport.Y = 0;
    mViewport.AspectRatio = ( float32 ) mViewport.Width / ( float32 ) mViewport.Height;
    SetViewport( mViewport );
}

PresentationParameters GraphicsDevice::GetPresentationParameters( void )
{
    return mPresentationParameters;
}

Viewport GraphicsDevice::GetViewport( void )
{
    return mViewport;
}

void GraphicsDevice::SetRenderTarget( const RenderTarget2D* render_target )
{
    mRenderTarget = ( render_target == NULL ) ? mBackBuffer : ( RenderTarget2D* ) render_target;
    mDeviceContext->OMSetRenderTargets( 1, &(mRenderTarget->mRenderTargetView), NULL );
}

void GraphicsDevice::SetViewport( const Viewport& viewport )
{
    mViewport = viewport;

    D3D11_VIEWPORT tmp;
    ZeroMemory( &tmp, sizeof( D3D11_VIEWPORT ) );
    tmp.TopLeftX = ( float32 ) mViewport.X;
    tmp.TopLeftY = ( float32 ) mViewport.Y;
    tmp.Width = ( float32 ) mViewport.Width;
    tmp.Height = ( float32 ) mViewport.Height;
    tmp.MaxDepth = mViewport.MaxDepth;
    tmp.MinDepth = mViewport.MinDepth;

    mDeviceContext->RSSetViewports( 1, &tmp );
}
